Day 23: 비동기 프로그래밍
비동기란?
동기 코드는 작업이 끝날 때까지 기다리지만, 비동기 코드는 대기 시간에 다른 작업을 수행합니다.
async/await 기본
import asyncio
async def say_hello(name, delay):
await asyncio.sleep(delay)
print(f"안녕, {name}! ({delay}초 후)")
async def main():
# 순차 실행: 3초
await say_hello("철수", 1)
await say_hello("영희", 2)
asyncio.run(main())
동시 실행: gather
async def main():
# 동시 실행: 2초 (가장 긴 작업 기준)
await asyncio.gather(
say_hello("철수", 1),
say_hello("영희", 2),
say_hello("민수", 1.5),
)
asyncio.run(main())
동기 vs 비동기 비교
| 항목 | 동기 | 비동기 |
|---|---|---|
| 키워드 | def | async def |
| 호출 | func() | await func() |
| 대기 | 차단 (blocking) | 비차단 (non-blocking) |
| I/O 작업 | 순차적 | 동시 처리 가능 |
| 적합한 상황 | CPU 연산 | 네트워크, 파일 I/O |
비동기 HTTP 요청
import aiohttp
import asyncio
async def fetch(session, url):
async with session.get(url) as response:
return await response.json()
async def fetch_all(urls):
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
results = await asyncio.gather(*tasks)
return results
urls = [
"https://jsonplaceholder.typicode.com/posts/1",
"https://jsonplaceholder.typicode.com/posts/2",
"https://jsonplaceholder.typicode.com/posts/3",
]
results = asyncio.run(fetch_all(urls))
for r in results:
print(r["title"])
비동기 컨텍스트 매니저
class AsyncTimer:
def __init__(self, name):
self.name = name
async def __aenter__(self):
self.start = asyncio.get_event_loop().time()
return self
async def __aexit__(self, *args):
elapsed = asyncio.get_event_loop().time() - self.start
print(f"{self.name}: {elapsed:.2f}초")
async def main():
async with AsyncTimer("API 호출"):
await asyncio.sleep(1)
asyncio.run(main())
오늘의 연습문제
- 5개 URL을 동시에 요청하여 응답 시간을 측정하고 순차 실행과 비교하세요.
- 비동기 파일 읽기 (
aiofiles)를 사용하여 여러 파일을 동시에 읽으세요. - 비동기 생산자-소비자 패턴을
asyncio.Queue로 구현하세요.